import { Injectable, Logger } from '@nestjs/common';
import * as puppeteer from 'puppeteer';
import { PDFOptions, PaperFormat } from 'puppeteer';
import * as _ from 'lodash';
import { jwtDecode } from 'jwt-decode';
import * as fs from 'fs';

@Injectable()
export class ReportsService {
  private readonly logger = new Logger(ReportsService.name);
  private readonly MAX_BROWSER_START_ATTEMPTS = 5;
  private readonly RETRY_DELAY = 1000; // 1秒
  private readonly cookie = {
    domain:
      process.env.ENV === 'production' ? 'api.shijieu.cn' : 'api.t.shijieu.cn',
    name: 'XAUTH#sjsy#USER',
    value: '',
  };

  private readonly CACHE_DIR = '/root/autodl-tmp/.cache/puppeteer';

  private ensureCacheDirectory(): void {
    try {
      if (!fs.existsSync(this.CACHE_DIR)) {
        fs.mkdirSync(this.CACHE_DIR, { recursive: true });
        this.logger.log(`📁 创建缓存目录: ${this.CACHE_DIR}`);
      }
    } catch (error) {
      this.logger.warn(`⚠️ 创建缓存目录失败: ${error.message}`);
    }
  }

  private async launchBrowserWithRetry(): Promise<puppeteer.Browser> {
    let browser: puppeteer.Browser | null = null;

    for (let i = 0; i < this.MAX_BROWSER_START_ATTEMPTS; i++) {
      try {
        this.logger.log(
          `启动浏览器中 (第 ${i + 1}/${this.MAX_BROWSER_START_ATTEMPTS} 次尝试)...`,
        );

        // 确保缓存目录存在
        this.ensureCacheDirectory();

        browser = await puppeteer.launch({
          headless: true,
          args: [
            '--no-sandbox', // 禁用沙箱，解决某些环境下的权限问题
            '--disable-setuid-sandbox', // 禁用 setuid 沙箱，配合 no-sandbox 使用
            '--font-render-hinting=none', // 禁用字体渲染提示，确保跨平台一致性
            '--use-gl=desktop', // 提升 Canvas 渲染性能
            `--disk-cache-dir=${this.CACHE_DIR}`, // 共享磁盘缓存目录
            '--disk-cache-size=1073741824', // 设置缓存大小为 1GB
            '--aggressive-cache-discard', // 积极丢弃缓存以避免内存泄漏
            '--enable-features=NetworkService,NetworkServiceLogging', // 启用网络服务日志
            '--disable-background-timer-throttling', // 禁用后台定时器节流
            '--disable-backgrounding-occluded-windows', // 禁用背景遮挡窗口的后台处理
            '--disable-renderer-backgrounding', // 禁用渲染器后台处理
            '--disable-features=TranslateUI', // 禁用翻译 UI
            '--disable-ipc-flooding-protection', // 禁用 IPC 洪水保护
          ],
          defaultViewport: { width: 1200, height: 800 },
          // 清理策略：
          // ​自动清理：正常情况下，Puppeteer 会在浏览器实例关闭后自动删除此目录。
          // ​残留情况：如果进程异常退出（如崩溃或被强制终止），目录可能残留，需手动清理。
          // ​自定义用户数据目录：/tmp/puppeteer_dev_chrome_profile
          // 注意，macos 下没有 /dev/null 目录
          // userDataDir: '/dev/null',
          timeout: 300000, // 浏览器启动超时时间，单位为毫秒，这里设置为 5 分钟
          protocolTimeout: 300000, // 协议超时时间，防止 Runtime.callFunctionOn 超时，单位为毫秒，这里设置为 5 分钟
        });

        this.logger.log('浏览器启动成功');
        return browser;
      } catch (err) {
        this.logger.error(`浏览器启动失败 (尝试 ${i + 1}):`, err);

        if (browser) {
          try {
            await browser.close();
          } catch (closeErr) {
            this.logger.error('关闭失败的浏览器实例时出错:', closeErr);
          }
        }

        if (i === this.MAX_BROWSER_START_ATTEMPTS - 1) {
          throw new Error(
            `浏览器启动失败，已重试 ${this.MAX_BROWSER_START_ATTEMPTS} 次: ${err.message}`,
          );
        }

        await this.sleep(this.RETRY_DELAY);
      }
    }
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  /**
   * 清理缓存目录中的过期文件
   * @param maxAge 文件最大保留时间（毫秒），默认 24 小时
   */
  private cleanupCache(maxAge: number = 24 * 60 * 60 * 1000): void {
    try {
      if (!fs.existsSync(this.CACHE_DIR)) {
        return;
      }

      const files = fs.readdirSync(this.CACHE_DIR);
      const now = Date.now();

      files.forEach((file) => {
        const filePath = `${this.CACHE_DIR}/${file}`;
        try {
          const stats = fs.statSync(filePath);
          if (now - stats.mtime.getTime() > maxAge) {
            fs.unlinkSync(filePath);
            this.logger.log(`🧹 清理过期缓存文件: ${file}`);
          }
        } catch (error) {
          this.logger.warn(`⚠️ 清理缓存文件失败: ${file} - ${error.message}`);
        }
      });
    } catch (error) {
      this.logger.warn(`⚠️ 清理缓存目录失败: ${error.message}`);
    }
  }

  async generatePdf(
    params: any,
    headers: any,
  ): Promise<any | Record<string, any>> {
    const xId = headers['x-request-id'];
    const userCookie = headers['user-cookie'];

    this.logger.log(`Object.keys(headers): ${Object.keys(headers)}`);
    this.logger.log(`xId: ${xId}`);

    this.logger.log('-----------开始-----------');
    this.logger.log(`-----------params---------`);
    this.logger.log(params);
    this.logger.log(`-----------headers---------`);
    this.logger.log(headers);

    this.cookie.value = userCookie;
    this.logger.log('调用所使用的 cookie 如下:' + this.cookie.value);

    // 解码JWT并打印
    try {
      if (this.cookie.value) {
        // 解码JWT获取数据
        const decodedJwt = jwtDecode(this.cookie.value);
        this.logger.log('解码后的 JWT 数据:');
        this.logger.log(JSON.stringify(decodedJwt, null, 2));
      } else {
        this.logger.warn('Cookie 为空，无法解码 JWT');
      }
    } catch (error) {
      this.logger.error(`JWT 解码失败: ${error.message}`);
    }

    try {
      // 清理过期缓存（每次生成 PDF 前清理一次）
      this.cleanupCache();

      const browser = await this.launchBrowserWithRetry();

      const res = await this.print(browser, params, this.cookie);

      await browser.close();

      // 简化错误检查：如果有错误信息则抛出异常
      if (res.error) {
        throw new Error(res.error);
      }

      return res;
    } catch (err) {
      this.logger.error('PDF生成过程中发生错误:', err);
      throw err;
    }
  }

  private async print(
    browser: puppeteer.Browser,
    params: any,
    cookie: any,
  ): Promise<any> {
    this.logger.log(`开始打印`);
    // 优化后的错误处理链路：
    // 1. 使用字符串统一收集各种错误信息
    // 2. 错误来源：页面运行错误、页面主动报告错误、页面状态错误、调试模式下的页面错误和关键请求失败
    // 3. 多检查点设计：在页面加载后、异步等待后、轮询前、轮询后分别检查错误状态
    // 4. 立即终止策略：发现页面错误或关键请求失败立即返回 500 错误，避免浪费资源
    // 5. 统一返回格式：{ error: string } 或直接返回 PDF 数据
    // 6. 避免直接抛出异常，统一在 generatePdf 中处理
    let printError: string = '';

    const { timeout, isDebug = '1', isBookCover = false } = params;

    // 日志级别：2=verbose(详细), 1=simple(简单), 0=none(无日志)
    const debugLevel = parseInt(isDebug) || 0;

    const queryParams = _.omit(params, ['timeout', 'isDebug', 'isBookCover']);
    const queryString = _.map(
      queryParams,
      (value, key) => `${key}=${value}`,
    ).join('&');

    const page = await browser.newPage();
    const baseUrl = process.env.PRINT_URL;
    const routePathPrint = process.env.ROUTE_PATH_PRINT || '/reports-print';
    const routePathBookCover =
      process.env.ROUTE_PATH_BOOK_COVER || '/reports-print/book-cover';

    // 日志级别控制
    if (debugLevel >= 1) {
      this.logger.log(`🔧 环境配置检查:`);
      this.logger.log(`   PRINT_URL: ${baseUrl}`);
      if (debugLevel >= 2) {
        this.logger.log(`   ROUTE_PATH_PRINT: ${routePathPrint}`);
        this.logger.log(`   ROUTE_PATH_BOOK_COVER: ${routePathBookCover}`);
      }
      this.logger.log(`   isBookCover: ${isBookCover}`);
      this.logger.log(`   调试级别: ${debugLevel}`);
    }

    if (!baseUrl) {
      this.logger.error('❌ PRINT_URL 环境变量未配置');
      throw new Error('PRINT_URL 未配置');
    }

    // 根据 isBookCover 参数选择不同的路径
    const printUrl = `${baseUrl}${isBookCover ? routePathBookCover : routePathPrint}?${queryString}`;

    if (debugLevel >= 1) {
      this.logger.log(`🌐 构造的完整 URL:`);
      this.logger.log(`   ${printUrl}`);
      if (debugLevel >= 2) {
        this.logger.log(`🔗 查询参数:`);
        this.logger.log(`   ${queryString}`);
      }
    }

    try {
      await page.setCookie(cookie);
      if (debugLevel >= 2) {
        this.logger.log(`🍪 Cookie 设置成功`);
      }
    } catch (cookieError) {
      this.logger.error(`❌ Cookie 设置失败: ${cookieError}`);
      throw new Error(`Cookie 设置失败: ${cookieError.message || cookieError}`);
    }

    // 控制台日志监听（仅在 verbose 模式下启用）
    if (debugLevel >= 2) {
      page.on('console', async (message) => {
        const type = message.type();

        // 获取所有参数
        const args = message.args();

        // 序列化参数
        const values = await Promise.all(
          args.map(async (arg) => {
            try {
              // 尝试获取 JSON 值
              const val = await arg.jsonValue();
              // 对对象进行序列化
              if (typeof val === 'object' && val !== null) {
                return JSON.stringify(val, null, 2);
              }
              return val;
            } catch (error) {
              return `[无法序列化: ${error.message}]`;
            }
          }),
        );

        // 打印序列化后的消息
        this.logger.log(`📄 页面日志 [${type}]: ${values.join(' ')}`);
      });
    }

    // 默认超时1分钟
    const customTimeout = timeout ? Number(timeout) : 60 * 1000;
    this.logger.log('customTimeout:', customTimeout);

    // 始终监听关键错误事件，不限于调试模式
    page.on('error', (error) => {
      this.logger.error(`🔥 页面运行错误: ${error.message || error}`);
      printError = `页面运行错误: ${error}`;
    });

    // 始终监听页面 JavaScript 错误
    page.on('pageerror', ({ message }) => {
      // 忽略特定的 React 错误
      if (
        message.includes('Minified React error #418') ||
        message.includes('Minified React error #423')
      ) {
        this.logger.warn(`⚠️ 忽略已知 React 错误: ${message}`);
        return;
      }

      this.logger.error(`🚫 页面 JavaScript 错误: ${message}`);
      if (!printError) {
        printError = `页面 JavaScript 错误: ${message}`;
      }
    });

    // 监听请求成功，用于调试
    if (debugLevel >= 1) {
      page.on('requestfinished', (request) => {
        const response = request.response();
        if (response) {
          const url = request.url();
          const status = response.status();
          const resourceType = request.resourceType();

          // simple 模式：只记录关键资源，verbose 模式：记录所有资源
          const shouldLog =
            debugLevel >= 2 ||
            url.includes('/reports-print') ||
            resourceType === 'document';

          if (shouldLog) {
            this.logger.log(
              `✅ 请求成功: ${url} (状态: ${status}, 类型: ${resourceType})`,
            );
          }
        }
      });
    }

    // 始终监听关键请求失败
    page.on('requestfailed', (request) => {
      const failedUrl = request.url();
      const errorText = request.failure()?.errorText || '未知错误';
      const method = request.method();
      const resourceType = request.resourceType();

      // 详细记录请求失败信息（仅在调试模式下）
      if (debugLevel >= 1) {
        this.logger.error(`❌ 请求失败详情:`);
        this.logger.error(`   URL: ${failedUrl}`);
        this.logger.error(`   错误: ${errorText}`);

        if (debugLevel >= 2) {
          this.logger.error(`   方法: ${method}`);
          this.logger.error(`   资源类型: ${resourceType}`);
        }
      }

      // 检查是否为 /reports-print/ 路径下的资源
      if (failedUrl.includes('/reports-print')) {
        // 判断是否为核心资源类型（html、css、js）
        const isCoreResource =
          resourceType === 'document' ||
          resourceType === 'stylesheet' ||
          resourceType === 'script';

        if (isCoreResource && !printError) {
          // 核心资源失败，终止 PDF 生成
          this.logger.error(`🚨 关键请求失败，终止 PDF 生成: ${errorText}`);
          printError = `关键资源加载失败: ${errorText} (URL: ${failedUrl})`;
        } else {
          // 非核心资源失败，仅记录警告
          this.logger.warn(
            `⚠️ 非核心资源加载失败: ${failedUrl} - ${errorText} (类型: ${resourceType})`,
          );
        }
      }
    });

    const timeGoTo = Date.now();
    if (debugLevel >= 1) {
      this.logger.log(`📍 开始导航到页面: ${printUrl}`);
    }

    try {
      await page.goto(printUrl, {
        // 从页面url输入开始到页面渲染完成一共等待 60 秒，如果超时则返回错误
        timeout: customTimeout,
        // 等待页面网络请求完全空闲，即没有正在进行的网络请求
        // networkidle0 表示在 500ms 内没有任何网络连接时认为导航完成
        // 参考: https://pptr.dev/api/puppeteer.page.goto#remarks
        waitUntil: 'networkidle0',
      });

      const timeAfterGoTo = Date.now();
      if (debugLevel >= 1) {
        this.logger.log(
          `✅ 页面导航完成，耗时: ${(timeAfterGoTo - timeGoTo) / 1000}秒`,
        );
      }
    } catch (gotoError) {
      this.logger.error(`❌ 页面导航失败: ${gotoError.message || gotoError}`);
      await page.close();
      return { error: `页面导航失败: ${gotoError.message || gotoError}` };
    }

    // 检查页面加载后是否有错误，如果有则立即返回错误信息
    if (printError) {
      this.logger.error(`🚨 页面加载后发现错误，终止后续逻辑: ${printError}`);
      await page.close();
      return { error: printError };
    }
    if (debugLevel >= 2) {
      this.logger.log(`📋 页面初次加载完成，未发现错误`);
    }

    // 等待一小段时间，让异步的页面错误和请求失败事件有机会触发
    if (debugLevel >= 2) {
      this.logger.log(`⏰ 等待异步资源加载完成... (300ms)`);
    }
    await this.sleep(300);

    // 再次检查是否有异步错误，确保页面错误和请求失败能被及时捕获
    if (printError) {
      this.logger.error(`🚨 检测到异步错误，终止后续逻辑: ${printError}`);
      await page.close();
      return { error: printError };
    }
    if (debugLevel >= 2) {
      this.logger.log(`📋 异步资源加载检查完成，未发现错误`);
    }

    page.setDefaultTimeout(customTimeout);

    // 根据 isBookCover 参数决定是否需要等待页面准备就绪
    let pageConfig: PDFOptions;

    if (isBookCover) {
      if (debugLevel >= 2) {
        this.logger.log('📖 开始书皮封面 pageConfig 计算');
      }
      // 计算与预览页面相同比例的尺寸
      // 预览页面比例 2460:1684 ≈ 1.46:1
      // 保持高度 246mm 不变，根据比例计算宽度
      // 246 * (2460/1684) ≈ 357mm
      pageConfig = {
        width: '357mm', // 根据预览页面比例计算的宽度
        height: '246mm', // 保持原有高度
        margin: {
          top: '0',
          right: '0',
          bottom: '0',
          left: '0',
        },
        preferCSSPageSize: false, // 使用我们指定的页面大小，而不是 CSS 中的大小
      };
      if (debugLevel >= 2) {
        this.logger.log('📐 使用书皮封面纸张尺寸:', pageConfig);
      }

      // 设置视口大小
      // 根据 240mm 对应 1684px 的算法计算
      // 1mm 对应 1684/240 ≈ 7.02px
      // 357mm * 7.02 ≈ 2506px
      // 246mm * 7.02 ≈ 1727px
      await page.setViewport({
        width: 2506,
        height: 1727,
        deviceScaleFactor: 1,
      });

      if (debugLevel >= 1) {
        this.logger.log('📚 书皮封面页面资源加载完成');
      }

      // 书皮封面模式下也需要检查错误
      if (printError) {
        this.logger.error(
          `书皮封面模式下发现错误，终止后续逻辑: ${printError}`,
        );
        await page.close();
        return { error: printError };
      }
    } else {
      if (debugLevel >= 2) {
        this.logger.log(
          '🔄 开始50ms轮询 shijieReports.isReady 或 shijieReports.errors',
        );
      }

      // 等待页面准备就绪或出现错误
      // 轮询检查 window.shijieReports 状态：
      // - isReady: true 表示页面渲染完成，可以生成 PDF
      // - errors: 非空数组表示页面渲染过程中出现错误
      await page.waitForFunction(
        () => {
          const reports = (window as any).shijieReports;
          return (
            reports &&
            (reports.isReady === true ||
              (reports.errors && reports.errors.length > 0))
          );
        },
        {
          polling: 50,
          timeout: customTimeout, // 使用自定义超时时间
        },
      );

      // 轮询完成后立即检查是否有异步错误
      if (printError) {
        this.logger.error(`轮询完成后发现错误，终止后续逻辑: ${printError}`);
        await page.close();
        return { error: printError };
      }

      // 检查页面状态，统一错误处理链路
      const pageStatus = await page.evaluate(() => {
        console.log('开始 evaluate');
        const reports = (window as any).shijieReports;
        if (!reports) {
          return { error: '页面未找到 shijieReports 对象' };
        }

        if (reports.errors?.length > 0) {
          const errorMessage = Array.isArray(reports.errors)
            ? reports.errors.join('; ')
            : reports.errors || '页面报告了未知错误';
          return { error: errorMessage };
        }

        if (reports.isReady === true) {
          return { error: '' };
        }

        return { error: '页面状态异常：isReady 不为 true' };
      });

      // 统一错误处理：不直接抛出异常，而是返回错误信息
      if (pageStatus.error) {
        await page.close();
        return { error: `页面处理失败: ${pageStatus.error}` };
      }

      if (debugLevel >= 1) {
        this.logger.log('✨ 看到这条说明页面准备就绪，可以打印了');
      }

      // 获取页面配置
      pageConfig = await page.evaluate(() => {
        // 声明 Window 接口扩展以支持 shijieReports
        interface CustomWindow extends Window {
          shijieReports?: {
            isReady: boolean;
            reportConfig?: {
              personalized?: {
                page?: {
                  size?: string | { width: string; height: string };
                  bleed?: string; // 新增 bleed 配置支持
                };
              };
            };
          };
        }

        // 使用类型断言
        const customWindow = window as CustomWindow;

        // 默认配置
        const defaultConfig = { format: 'A4' as PaperFormat };

        try {
          if (!customWindow.shijieReports?.reportConfig?.personalized?.page) {
            return defaultConfig;
          }

          const pageConfig =
            customWindow.shijieReports.reportConfig.personalized.page;
          const pageSize = pageConfig.size || 'A4';
          const bleed = pageConfig.bleed || '0mm'; // 获取 bleed 配置，默认为 0mm

          /**
           * 解析尺寸字符串，支持 mm、px、in 等单位
           * @param sizeStr 尺寸字符串，如 "170mm"、"200px"
           * @returns 解析后的数值和单位，如 { value: 170, unit: 'mm' }
           */
          const parseSize = (
            sizeStr: string,
          ): { value: number; unit: string } => {
            const match = sizeStr.match(/^(\d+(?:\.\d+)?)(mm|px|in|cm|pt)$/i);
            if (!match) {
              throw new Error(`无效的尺寸格式: ${sizeStr}`);
            }
            return {
              value: parseFloat(match[1]),
              unit: match[2].toLowerCase(),
            };
          };

          /**
           * 添加 bleed 到尺寸中
           * @param originalSize 原始尺寸字符串
           * @param bleedSize bleed 尺寸字符串
           * @returns 添加 bleed 后的尺寸字符串
           */
          const addBleedToSize = (
            originalSize: string,
            bleedSize: string,
          ): string => {
            try {
              const original = parseSize(originalSize);
              const bleedParsed = parseSize(bleedSize);

              // 确保单位一致
              if (original.unit !== bleedParsed.unit) {
                console.warn(
                  `尺寸单位不一致: ${originalSize} vs ${bleedSize}，使用原始尺寸单位`,
                );
              }

              // bleed 需要在四边都加上，所以总尺寸是原尺寸 + 2 * bleed
              const finalSize = original.value + 2 * bleedParsed.value;
              return `${finalSize}${original.unit}`;
            } catch (error) {
              console.warn(`处理 bleed 时出错: ${error.message}，使用原始尺寸`);
              return originalSize;
            }
          };

          // 标准纸张格式列表（包括大小写形式）
          const standardFormats = [
            'A0',
            'A1',
            'A2',
            'A3',
            'A4',
            'A5',
            'A6',
            'LETTER',
            'LEGAL',
            'TABLOID',
            'LEDGER',
            'Letter',
            'Legal',
            'Tabloid',
            'Ledger',
          ];

          // 检查是否包含 landscape 或 portrait 模式
          const isLandscape =
            typeof pageSize === 'string' &&
            pageSize.toLowerCase().includes('landscape');
          const isPortrait =
            typeof pageSize === 'string' &&
            pageSize.toLowerCase().includes('portrait');

          // 如果包含 landscape 或 portrait，提取纸张格式
          if (isLandscape || isPortrait) {
            const format = pageSize
              .toLowerCase()
              .replace('landscape', '')
              .replace('portrait', '')
              .trim();

            // 检查提取的格式是否是标准格式
            if (
              standardFormats
                .map((f) => f.toLowerCase())
                .includes(format.toLowerCase())
            ) {
              console.log(
                `使用标准纸张格式: ${format} (${isLandscape ? '横向' : '纵向'})`,
              );
              // 标准格式不支持 bleed，因为 puppeteer 的标准格式是固定尺寸
              if (bleed !== '0mm') {
                console.warn(
                  `标准纸张格式不支持 bleed 配置，忽略 bleed: ${bleed}`,
                );
              }
              return {
                format: format as PaperFormat,
                landscape: isLandscape,
              };
            }
          }

          // 检查是否是标准纸张格式
          if (
            typeof pageSize === 'string' &&
            standardFormats.includes(pageSize as PaperFormat)
          ) {
            console.log(`使用标准纸张格式: ${pageSize} (纵向)`);
            // 标准格式不支持 bleed，因为 puppeteer 的标准格式是固定尺寸
            if (bleed !== '0mm') {
              console.warn(
                `标准纸张格式不支持 bleed 配置，忽略 bleed: ${bleed}`,
              );
            }
            return { format: pageSize as PaperFormat, landscape: false };
          }

          // 解析自定义尺寸，例如 '170mm 240mm'
          if (
            typeof pageSize === 'string' &&
            pageSize.includes(' ') &&
            pageSize.toLowerCase().includes('mm')
          ) {
            try {
              const [width, height] = pageSize
                .split(' ')
                .map((dim) => dim.trim());
              if (width && height) {
                // 添加 bleed 到宽度和高度
                const finalWidth = addBleedToSize(width, bleed);
                const finalHeight = addBleedToSize(height, bleed);

                console.log(`原始纸张尺寸: ${width} x ${height}`);
                if (bleed !== '0mm') {
                  console.log(`bleed 配置: ${bleed}`);
                  console.log(
                    `最终纸张尺寸（含 bleed）: ${finalWidth} x ${finalHeight}`,
                  );
                } else {
                  console.log(`最终纸张尺寸: ${finalWidth} x ${finalHeight}`);
                }

                return {
                  width: finalWidth,
                  height: finalHeight,
                };
              }
            } catch (error) {
              console.warn(`解析自定义纸张尺寸失败: ${pageSize}`);
            }
          }

          // 如果是对象形式的自定义尺寸
          if (
            typeof pageSize === 'object' &&
            pageSize &&
            pageSize.width &&
            pageSize.height
          ) {
            // 添加 bleed 到宽度和高度
            const finalWidth = addBleedToSize(pageSize.width, bleed);
            const finalHeight = addBleedToSize(pageSize.height, bleed);

            console.log(
              `原始纸张尺寸对象: ${pageSize.width} x ${pageSize.height}`,
            );
            if (bleed !== '0mm') {
              console.log(`bleed 配置: ${bleed}`);
              console.log(
                `最终纸张尺寸（含 bleed）: ${finalWidth} x ${finalHeight}`,
              );
            } else {
              console.log(`最终纸张尺寸: ${finalWidth} x ${finalHeight}`);
            }

            return {
              width: finalWidth,
              height: finalHeight,
            };
          }

          return defaultConfig;
        } catch (error) {
          console.error('获取页面配置时出错:', error);
          return defaultConfig;
        }
      });
    }

    const timeReady = Date.now();

    // 生成 PDF，添加错误处理
    try {
      const outPutPdf = await page.pdf({
        // 设置 PDF 生成超时时间
        timeout: customTimeout,
        // 打印背景图片和颜色 会增大文件体积
        // 文字颜色、边框颜色、文本阴影等非“背景”属性仍会正常打印。
        // 所有页面元素上的背景相关 CSS 样式（background-color、background-image、background 等）
        // 都不会被渲染到生成的 PDF 里，无论这些样式是内联写的，还是在外部样式表中定义的，都不会生效。
        printBackground: true,
        // 不使用 CSS 中的页面大小设置，而是使用 pageConfig 中指定的尺寸
        preferCSSPageSize: false,
        ...(pageConfig as PDFOptions),
      });

      const timePrintFinish = Date.now();

      if (debugLevel >= 1) {
        this.logger.log(`🎯 PDF 生成完成`);
        if (debugLevel >= 2) {
          this.logger.log(
            `⏱️ 从访问页面到页面就绪花了 ${(timeReady - timeGoTo) / 1000} 秒`,
          );
          this.logger.log(
            `🚀 从页面就绪到打印完成花了 ${(timePrintFinish - timeReady) / 1000} 秒`,
          );
          this.logger.log(
            `📊 总耗时: ${(timePrintFinish - timeGoTo) / 1000} 秒`,
          );
        }
      }

      // 记录页面性能信息
      try {
        const metrics = await page.metrics();
        if (debugLevel >= 2) {
          this.logger.log(`📈 页面性能指标:`);
          this.logger.log(
            `   JSHeapUsedSize: ${Math.round(metrics.JSHeapUsedSize / 1024 / 1024)}MB`,
          );
          this.logger.log(
            `   JSHeapTotalSize: ${Math.round(metrics.JSHeapTotalSize / 1024 / 1024)}MB`,
          );
          this.logger.log(`   LayoutCount: ${metrics.LayoutCount}`);
          this.logger.log(`   RecalcStyleCount: ${metrics.RecalcStyleCount}`);
        }
      } catch (metricsError) {
        this.logger.warn(`⚠️ 获取页面性能指标失败: ${metricsError.message}`);
      }

      await page.close();

      return outPutPdf;
    } catch (pdfError) {
      this.logger.error('PDF 生成失败:', pdfError);
      await page.close();
      return { error: `PDF 生成失败: ${pdfError.message || pdfError}` };
    }
  }
}
